# Copyright 2010 Thomas Conneely
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

def frange(start, stop, step=1):
    '''Generates a list of numbers, handling float increments.
    Also supports counting down (set start > stop)
    
    Args:
     @ start - the starting value
     @ stop - the max value
     @ step - the increment to use (default = 1)
    
    Note: Unlike range() this includes the stop value
    in the list
    
    Note: If total number of steps is not an integer 
    multiple of step the final value will be a number
    < stop (but as close as possible).
    
    Example:
        x = list(frange.frange(1, 3, 0.5))
        x == [1.0, 1.5, 2.0, 2.5, 3.0]
        
        y = list(frange.frange(3, 1, 0.5))
        y == [3.0, 2.5, 2.0, 1.5, 1.0]
        
        z = list(frange.frange(3, 1, -0.5))
        z == [3.0, 2.5, 2.0, 1.5, 1.0]
        
        s = list(frange.frange(3, 3, 0.5))
        s == [3.0]
    '''
     
    if start == stop:
        n = 0
        
    elif step == 0:
        raise ValueError('Step cannot be zero')
        
    elif start < stop:
        n = int((stop - start) / abs(step))
        step = abs(step)
        
    elif start > stop:
        n = int((start - stop) / abs(step))
        step = -abs(step)
        
    i = 0
    while i <= n:
        yield start + i * step
        i += 1
    raise StopIteration

# In [121]: %timeit list(foo.frange(0,100,0.1))
# 1000 loops, best of 3: 472 us per loop